//********************************************************************
//	created:	12:10:2011   20:42
//	filename: 	httprequest.cpp
//	author:		tiamo
//	purpose:	http request
//********************************************************************

#include "stdafx.h"
#include "httprequest.h"

namespace utils
{
	//
	// reset
	//
	void HttpRequest::reset()
	{
		if(mHandle)
			curl_easy_cleanup(mHandle);

		mHandle																= nullptr;
		mContent.reset();
	}

	//
	// write function
	//
	size_t HttpRequest::writeRoutine(char const* inputData, size_t size, size_t count, utils::DataBuffer* outputData)
	{
		size_t totalLength													= size * count;
		outputData->appendData(inputData, static_cast<uint32_t>(totalLength));
		return totalLength;
	}

	////
	//// debug routine
	////
	//int HttpRequest::debugRoutine(CURL* theHandle, curl_infotype infoType, char* messageBuffer, size_t messageSize, void* context)
	//{
	//	LogInfo(L"HttpRequest(%p): (%u) %ls", context, infoType, utils::utf8ToWide(messageBuffer, messageSize).c_str());
	//	return 0;
	//}

	//
	// begin request
	//
	void HttpRequest::beginRequest(wchar_t const* remoteURLFormat, ...)
	{
		va_list argList;
		va_start(argList, remoteURLFormat);
		wchar_t remoteURL[1024]												= {0};
		platform::vsnwprintf(remoteURL, ARRAYSIZE(remoteURL), remoteURLFormat, argList);
		va_end(argList);

		mRemoteURL															= remoteURL;
		mPostData.clear();
		mCookie.clear();
	}

	//
	// do request imp
	//
	void HttpRequest::doRequestImp()
	{
		reset();

		mHandle																= curl_easy_init();
		if(!mHandle)
			ThrowException(L"HttpRequest(%p): unable to create easy handle", this);

		if(mRemoteURL.empty())
			ThrowException(L"HttpRequest(%p): invalid remote url", this);

		if(curl_easy_setopt(mHandle, CURLOPT_URL, (utils::wideToUtf8(mRemoteURL).c_str())))
			ThrowException(L"HttpRequest(%p): unable to set remote URL", this);

		//if(curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1))
		//	ThrowException(L"HttpRequest(%p): unable to set verbose mode", this);

		//if(curl_easy_setopt(mHandle, CURLOPT_DEBUGFUNCTION, &HttpRequest::debugRoutine))
		//	ThrowException(L"HttpRequest(%p): unable to debug routine", this);

		//if(curl_easy_setopt(mHandle, CURLOPT_DEBUGDATA, this))
		//	ThrowException(L"HttpRequest(%p): unable to set debug data", this);

		if(curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, 0))
			ThrowException(L"HttpRequest(%p): unable to set https verify peer", this);

		if(curl_easy_setopt(mHandle, CURLOPT_CAINFO, "cacert.pem"))
			ThrowException(L"HttpRequest(%p): unable to set ca cert", this);

		if(curl_easy_setopt(mHandle, CURLOPT_TIMEOUT, 60))
			ThrowException(L"HttpRequest(%p): unable to set timeout", this);

		if(curl_easy_setopt(mHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1))
			ThrowException(L"HttpRequest(%p): unable to set http version", this);

		if(curl_easy_setopt(mHandle, CURLOPT_FORBID_REUSE, 1))
			ThrowException(L"HttpRequest(%p): unable to set http version", this);

		if(curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, &mContent))
			ThrowException(L"HttpRequest(%p): unable to set write data", this);

		if(curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, &HttpRequest::writeRoutine))
			ThrowException(L"HttpRequest(%p): unable to set write function", this);

		if(curl_easy_setopt(mHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"))
			ThrowException(L"HttpRequest(%p): unable to set user agent", this);

		if(curl_easy_setopt(mHandle, CURLOPT_FAILONERROR, true))
			ThrowException(L"HttpRequest(%p): unable to set error mode", this);

		if(curl_easy_setopt(mHandle, CURLOPT_FOLLOWLOCATION, false))
			ThrowException(L"HttpRequest(%p): unable to set redirection action", this);

		if(curl_easy_setopt(mHandle, CURLOPT_ENCODING, "deflate, gzip"))
			ThrowException(L"HttpRequest(%p): unable to set accept encoding", this);

		if(curl_easy_setopt(mHandle, CURLOPT_HTTP_CONTENT_DECODING, true))
			ThrowException(L"HttpRequest(%p): unable to set decoding method", this);

		if(!mCookie.empty() && curl_easy_setopt(mHandle, CURLOPT_COOKIE, (utils::wideToUtf8(mCookie).c_str())))
			ThrowException(L"HttpRequest(%p): unable to set cookie(%ls)", this, mCookie.c_str());

		if(!mPostData.empty() && curl_easy_setopt(mHandle, CURLOPT_POSTFIELDS, mPostData.c_str()))
			ThrowException(L"HttpRequest(%p): unable to set post data(%ls)", this, utils::utf8ToWide(mPostData).c_str());

		if(curl_easy_setopt(mHandle, CURLOPT_NOSIGNAL, 1))
			ThrowException(L"HttpRequest(%p): unable to set nosignal", this);

		CURLcode errorCode													= curl_easy_perform(mHandle);
		if(errorCode != CURLE_OK)
			ThrowException(L"HttpRequest(%p): unable to send http request(%d=%ls)", this, static_cast<int>(errorCode), utils::utf8ToWide(curl_easy_strerror(errorCode)).c_str());

		if(curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &mResult))
			ThrowException(L"HttpRequest(%p): unable to get response code", this);
	}

	//
	// get redirect location
	//
	UniString HttpRequest::getRedirectLocation()
	{
		if(!mHandle)
			ThrowException(L"HttpRequest(%p): invalid easy handle", this);

		if(mResult < 300 || mResult >= 400)
			ThrowException(L"HttpRequest(%p): not a redirect response code(%d)", this, mResult);

		char* redirectURL													= nullptr;
		if(curl_easy_getinfo(mHandle, CURLINFO_REDIRECT_URL, &redirectURL) || !redirectURL)
			ThrowException(L"HttpRequest(%p): unable to get redirect url", this);

		return utils::utf8ToWide(redirectURL);
	}

	//
	// url encoding
	//
	Utf8String HttpRequest::urlEncoding(Utf8String const& inputURL)
	{
		Utf8String retValue;
		char temp[4]														= {0};
		for(size_t i = 0; i < inputURL.length(); i ++)
		{
			uint8_t ch														= static_cast<uint8_t>(inputURL[i]);
			if( ch <= 0x1f || ch >= 0x7f || ch == '$' || ch == '&' || ch == '+' || ch == ',' || ch == '/' || ch == ':' || ch == ';' || ch == '=' || ch == '?' || ch == '@' || ch == ' ' || 
				ch == '"' || ch == '<' || ch == '>' || ch == '#' || ch == '%' || ch == '{' || ch == '}' || ch == '|' || ch == '\\' || ch == '^' || ch == '~' || ch == '[' || ch == ']' || ch == '`')
			{
				platform::snprintf(temp, ARRAYSIZE(temp), "%%%02X", ch);
				retValue.append(temp);
			}
			else
			{
				retValue.append(1, ch);
			}
		}

		return retValue;
	}
}
